////////////////////////////////////////////////////////////////////////////
//	Renderer.h & Renderer.cpp
//		Singleton that initializes Direct3D and calls the render processes
//		for all objects that are passed to its Render method in a set.
//
//	Author:					Daniel Lima - August 2012
/////////////////////////////////////////////////////////////////////////////

#ifndef _RENDERER_H_
#define _RENDERER_H_

// Direct3D 9 Libraries
#include <d3d9.h>
#include <d3dx9.h>
#pragma comment(lib, "d3d9.lib")
#pragma comment(lib, "d3dx9.lib")

// STL's Vector
#include <vector>
using std::vector;

// Forward declarations
class RenderInstance;

class Renderer
{
	//////////////////////////////////////////////////////////////////////////
	//	------------------------------------------------------------------	//
	//							DATA	MEMBERS								//
	//	------------------------------------------------------------------	//
	//////////////////////////////////////////////////////////////////////////

	// Singleton's static instance
	static Renderer* 	sm_pInstance;

	IDirect3D9*		 	m_pD3D;
	IDirect3DDevice9*	m_pDevice;

	D3DXMATRIX		 	m_matrixView;
	D3DXMATRIX		 	m_matrixProj;

	LONG				m_nWindowWidth;
	LONG				m_nWindowHeight;

	vector<RenderInstance*> m_vpRenderInstances;

	//////////////////////////////////////////////////////////////////////////
	//	------------------------------------------------------------------	//
	//							PRIVATE	FUNCTIONS							//
	//	------------------------------------------------------------------	//
	//////////////////////////////////////////////////////////////////////////


	// Default Constructor - Initialize all variables to safe values
	Renderer(void);

	// For a proper singleton, the trilogy needs to be defined
	Renderer(const Renderer&) {}					// Copy Constructor
	Renderer& operator=(const Renderer&) {}			// Assignment Operator
	~Renderer(void) {}								// Destructor

	// Initializes the view and the projection matrices
	void InitializeViewProj();

public:
	//////////////////////////////////////////////////////////////////////////
	//	------------------------------------------------------------------	//
	//							PUBLIC	FUNCTIONS							//
	//	------------------------------------------------------------------	//
	//////////////////////////////////////////////////////////////////////////

	// Used to access the singleton's instance
	static Renderer* GetInstance(void);

	// Used to delete the singleton's instance
	static void DeleteInstance(void);

	// Initialize the graphic engines with the correct window parameters
	int Initialize(HWND hWindow, bool bWindowed, bool bVSync);

	// Renders a batch of RenderNode instances contained in m_RenderSet
	void Render(void);

	// Clean up any allocated memory
	void Shutdown(void);

	int AddRenderInstance(int shaderID, int textureID, int meshID);

	void CameraRotate(float x, float y, float z);

	void CameraTranslate(float x, float y, float z);

	#pragma region Inline Functions
	// Clears one or more surfaces
	__forceinline void Clear(DWORD _dwFlags = D3DCLEAR_TARGET, D3DCOLOR _Color = D3DXCOLOR(0.1f, 0.1f, 0.1f, 1.0f),
		float _fDepth = 1.0f, DWORD _dwStencil = 0, DWORD _dwCount = 0, D3DRECT* _pRects = nullptr)
	{
		m_pDevice->Clear(_dwCount, _pRects, _dwFlags, _Color, _fDepth, _dwStencil);
	}

	// Begins a scene for rendering
	__forceinline void BeginScene(void)
	{
		m_pDevice->BeginScene();
	}

	// Ends a scene 
	__forceinline void EndScene(void)
	{
		m_pDevice->EndScene();
	}

	// Presents the next buffer.  Common use will set all parameters to 0.
	__forceinline void Present(const RECT *_pSrcRect = nullptr, const RECT *_pDestRect = nullptr, 
		HWND _hWindow = nullptr, const RGNDATA *_pDirtyRegion = nullptr)
	{
		m_pDevice->Present(_pSrcRect, _pDestRect, _hWindow, _pDirtyRegion);
	}

	// source - Location of the vertices to start copying
	// length - Number of bytes to copy
	__forceinline IDirect3DVertexBuffer9* CreateVertexBuffer(void* source, UINT length)
	{
		IDirect3DVertexBuffer9* buff = nullptr;
		m_pDevice->CreateVertexBuffer(length, 0, 0, D3DPOOL_MANAGED, &buff, 0);

		void* mem = 0;
		buff->Lock(0, 0, &mem, 0);
		memcpy(mem, source, length);
		buff->Unlock();

		return buff;
	}

	// source - Location of the indices to start copying
	// length - Number of bytes to copy
	__forceinline IDirect3DIndexBuffer9* CreateIndexBuffer(void* source, UINT length)
	{
		IDirect3DIndexBuffer9* buff = nullptr;
		m_pDevice->CreateIndexBuffer(length, 0, D3DFMT_INDEX32, D3DPOOL_MANAGED, &buff, 0);

		void* mem = 0;
		buff->Lock(0, 0, &mem, 0);
		memcpy(mem, source, length);
		buff->Unlock();

		return buff;
	}

	__forceinline IDirect3DVertexDeclaration9* CreateVertexDeclaration(D3DVERTEXELEMENT9* definition)
	{
		IDirect3DVertexDeclaration9* decl = nullptr;
		m_pDevice->CreateVertexDeclaration(definition, &decl);
		return decl;
	}
	
	__forceinline void SetVertexDeclaration(IDirect3DVertexDeclaration9* decl)
	{
		m_pDevice->SetVertexDeclaration(decl);
	}

	__forceinline void SetStreamSource(IDirect3DVertexBuffer9* buff, UINT vertSize)
	{
		m_pDevice->SetStreamSource(0, buff, 0, vertSize);
	}

	__forceinline void SetIndices(IDirect3DIndexBuffer9* buff)
	{
		m_pDevice->SetIndices(buff);
	}

	__forceinline void DrawIndexedPrimitive(D3DPRIMITIVETYPE type, UINT numVerts, UINT primCount)
	{
		m_pDevice->DrawIndexedPrimitive(type, 0, 0, numVerts, 0, 8);
	}

	__forceinline IDirect3DTexture9* LoadTexture(const char* filename)
	{
		IDirect3DTexture9* texture;
		D3DXCreateTextureFromFile(m_pDevice, filename, &texture);
		return texture;
	}

	__forceinline ID3DXEffect* LoadEffect(const char* filename)
	{
		ID3DXEffect* effect;
		D3DXCreateEffectFromFile(m_pDevice, filename, 0, 0 ,0, 0, &effect, 0);
		return effect;
	}
#pragma endregion

};

#define RENDERER Renderer::GetInstance()

#endif // _RENDERER_H_